/******************************************************************************
 * Filename : uc_hash.c
 * Created  : 2009-8-8 by franco.yin
 * Description -
 *
 ******************************************************************************/

#ifdef __cplusplus
extern "C" {
#endif

#include "uc_hash.h"

uchash_table_s *
uchash_create(int size, int (*create_key) (uchash_node_s *, void *), void *p)
{
	int i = 0;
	uchash_table_s *pTab = NULL;

	pTab = (uchash_table_s *)uc_malloc_memset(sizeof (uchash_table_s));
	if (pTab == NULL) {
		uc_err("malloc\n");
		return NULL;
	}

	/* zero for pTab in uc_malloc_memset */
	pTab->hash = (uclist_s *)uc_malloc(sizeof(uclist_s) * size);
	if (pTab->hash == NULL) {
		uc_err("malloc\n");
		uc_free(pTab);
		return NULL;
	}

	uclist_init(&(pTab->list));
	for (i = 0; i < size; i++) {
		uclist_init(&(pTab->hash[i]));
	}

	pTab->size = size;
	pTab->create_key = create_key;
	pTab->p = p;
	pTab->cnt = 0;
	return pTab;
}

void
uchash_free(uchash_table_s *pTab)
{
	uc_free(pTab->hash);
	uc_free(pTab);
}

int
uchash_add(uchash_node_s *pNode, uchash_table_s *pTab)
{
	int key = (pTab->create_key)(pNode, pTab->p);

	if ((key < 0) || (key >= pTab->size)) {
		uc_err("hash key[%d] error\n", key);
		return -1;
	}

	uclist_add_tail(&pNode->node_list, &(pTab->list));
	uclist_add_tail(&pNode->node_hash, &(pTab->hash[key]));
	pTab->cnt++;
	return 0;
}

int
uchash_del(uchash_node_s *pNode, uchash_table_s *pTab)
{
	uclist_del(&pNode->node_list);
	uclist_del(&pNode->node_hash);
	pTab->cnt--;
	return 0;
}

int
uchash_count(uchash_table_s *pTab)
{
	if(pTab == NULL) {
		return 0;
	}
	return pTab->cnt;
}

int
uchash_empty(uchash_table_s *pTab)
{
	return (pTab->cnt > 0 ? 0 : 1);
}

static uchash_node_s *
uchash_walk_fun(uchash_table_s *pTab, uclist_s *head, int (*func)(uchash_table_s *, uchash_node_s *, void *), void *p)
{
	ucnode_s *pNode = NULL;
	ucnode_s *pNodeTmp = NULL;
	uchash_node_s *pHashNode = NULL;

	uclist_for_each_safe(pNode, pNodeTmp, head) {
		pHashNode = uchash_entry(pNode, uchash_node_s, node_hash);
		if(func == NULL) {
			return pHashNode;
		} else {
			if (func(pTab, pHashNode, p) < 0) {
				return pHashNode;
			}
		}
	}

	return NULL;
}

uchash_node_s *
uchash_walk(uchash_table_s *pTab, int key, int (*func)(uchash_table_s *, uchash_node_s *, void *), void *p)
{
	if ((key >= 0) && (key < pTab->size)) {
		return uchash_walk_fun(pTab, &(pTab->hash[key]), func, p);
	} else if (key == pTab->size) {
		return uchash_walk_all(pTab, func, p);
	}

	return NULL;
}

uchash_node_s *
uchash_walk_all(uchash_table_s *pTab, int (*func)(uchash_table_s *, uchash_node_s *, void *), void *p)
{
	ucnode_s *pNode = NULL;
	ucnode_s *pNodeTmp = NULL;
	uchash_node_s *pHashNode = NULL;

	uclist_for_each_safe(pNode, pNodeTmp, &pTab->list) {
		pHashNode = uclist_entry(pNode, uchash_node_s, node_list);
		if(func == NULL) {
			return pHashNode;
		} else {
			if (func(pTab, pHashNode, p) < 0) {
				return pHashNode;
			}
		}
	}

	return NULL;
}

uchash_node_s *
uchash_walk_all_ext(uchash_table_s *pTab, int walk_cnt, int (*func)(uchash_table_s *, uchash_node_s *, void *, void *, void *), void *p1, void *p2, void *p3)
{
	ucnode_s *pNode = NULL;
	ucnode_s *pNodeTmp = NULL;
	uchash_node_s *pHashNode = NULL;
	int i = 0;

	uclist_for_each_safe(pNode, pNodeTmp, &pTab->list) {
		pHashNode = uclist_entry(pNode, uchash_node_s, node_list);
		if(func == NULL) {
			return pHashNode;
		} else if (func(pTab, pHashNode, p1, p2, p3) < 0) {
			return pHashNode;
		}
		if(walk_cnt > 0) {
			i++;
			if(i >= walk_cnt) {
				return NULL;
			}
		}
	}

	return NULL;
}

uchash_node_s *
uchash_get_first(uchash_table_s *pTab)
{
	ucnode_s *pNode = NULL;

	pNode = uclist_get_first(&pTab->list);
	if(pNode == NULL) {
		return NULL;
	}

	return uclist_entry(pNode, uchash_node_s, node_list);
}

uchash_node_s *
uchash_get_last(uchash_table_s *pTab)
{
	ucnode_s *pNode = NULL;

	pNode = uclist_get_last(&pTab->list);
	if(pNode == NULL) {
		return NULL;
	}

	return uclist_entry(pNode, uchash_node_s, node_list);
}

#ifdef __cplusplus
}
#endif
